package org.tlang.ast.list.type;

import org.tlang.ast.AST;
import org.tlang.ast.ASTLeaf;
import org.tlang.ast.ASTList;
import org.tlang.context.Location;
import org.tlang.context.SymbolTable;
import org.tlang.context.TypeTable;
import org.tlang.exception.EvalException;
import org.tlang.exception.TypeException;
import org.tlang.type.ArrayType;
import org.tlang.type.Type;

import java.util.List;

public class TypeTag extends ASTList {
    private int nest;
    private int index;

    public TypeTag(List<AST> children) {
        super(children);
    }

    private String name() {
        return ((ASTLeaf) child(0)).token().getText();
    }

    private int dimension() {
        return numChildren() - 1;
    }

    @Override
    public void lookup(SymbolTable symbolTable, TypeTable typeTable) {
        if (Type.isPreDefinedType(name())) {
            return;
        }

        Location location = symbolTable.where(name());
        if (location == null) {
            throw new EvalException("type is not defined: " + name(), this);
        }

        this.nest = location.nest();
        this.index = location.index();
    }

    @Override
    public Type typeCheck(TypeTable typeTable) throws TypeException {
        return type(typeTable);
    }

    public Type type(TypeTable typeTable) {
        if (Type.isPreDefinedType(name())) {
            Type elementType = Type.tranStrToType(name(), this);

            if (dimension() == 0) {
                return elementType;
            } else {
                return new ArrayType(elementType, dimension());
            }
        } else {
            return typeTable.get(nest, index);
        }
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder("(").append(name());
        for (int i = 0; i < dimension(); i++) {
            stringBuilder.append("[]");
        }
        stringBuilder.append(")");
        return stringBuilder.toString();
    }
}
